#include "cnetworkthread.h"
#include "const_define.h"
#include "cmd_parse/cmd.h"
#include "serversetting.h"
#include "ctcpupdate.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>

#define MODE_UPDATE_NONE   "none"
#define MODE_UPDATE_WEB    "web"
#define MODE_UPDATE_APP    "app"
#define MODE_UPDATE_ALL    "all"

#define REBOOT_INTERVAL_SECONDS 3600*24*2
#define MAX_CONNECT_FAIL_TIMES  20

static int g_connect_fail_times = 0;
static QString ip_update = "http://120.24.229.221:9999/";

bool CNetworkThread::gServerConnected = false;
///
/// \brief CTcpConnect::CTcpConnect
/// \param parent
///
CTcpConnect::CTcpConnect(QObject *parent) :
    QObject(parent)
{
    isSetUpPorject = false;
    _tcpSocket = NULL;

    _updateMode = MODE_UPDATE_NONE;

    _need_update_all = false;
    _index_all_map = 0;

    connect(&_cmdParse,SIGNAL(s_XmlReport(QByteArray)),this,SLOT(doWriteTcpDataAppendSharp(QByteArray)));
    connect(&_cmdParse,SIGNAL(s_JsonReport(QString)),this,SLOT(doGatewayStatusUpdate(QString)));
    connect(&_cmdParse,SIGNAL(s_deviceJsonReport(QString,QString)),
            this,SLOT(doGatewayDeviceStatusUpdate(QString,QString)));

    connect(&_cmdParse,SIGNAL(s_updateMode(QString)),this,SLOT(doChangeUpdateMode(QString)));
    connect(&_cmdParse,SIGNAL(s_controlCmd(QString)),this,SLOT(doControlCmd(QString)));

    _aliveTimer.setInterval(20000);//MSEC_STATE_CHECK);
    connect(&_aliveTimer,SIGNAL(timeout()),this,SLOT(doStateCheck()));
    _aliveTimer.start();

    _deviceUpdateTimer.setInterval(1200);
    connect(&_deviceUpdateTimer,SIGNAL(timeout()),this,SLOT(doUpdateDeviceStatus()));
    _deviceUpdateTimer.start();

    _http = new CHttpManger(this);
    connect(_http,SIGNAL(s_reply(QByteArray)),this,SLOT(doReadProjectSetting(QByteArray)));

    _httpUpdate = new CHttpManger(this);
    connect(_httpUpdate,SIGNAL(s_reply(QByteArray)),this,SLOT(doGatewayUpdateResult(QByteArray)));

}

CTcpConnect::~CTcpConnect()
{
    if(_tcpSocket)
        delete _tcpSocket;

    delete _http;
    delete _httpUpdate;
}

void CTcpConnect::doStateCheck()
{
     qDebug() << "doStateCheck:" << _tcpSocket->state() << " , _updateMode = " << _updateMode;

     if(!ServerSetting::defaultSetting()->keyVerifyOK)
     {
         qDebug() << "doStateCheck: serial key not found !" ;
         return ;
     }
//    static int onOff = 0;
//    if(onOff ++ %2 == 0)
//        _cmdParse->anayseJsonData("{\"Gate_SET\": {\"id\": \"1\",\"type\": \"switch\",\"action\": \"on\", \"state_value\": \"1\" } }");
//    else
//        _cmdParse->anayseJsonData("{\"Gate_SET\": {\"id\": \"1\",\"type\": \"switch\",\"action\": \"off\", \"state_value\": \"0\" } }");

//    _cmdParse->anayseJsonData("{ \"Gate_GET\":[\"a1\",\"a2\"]}");

//    return ;

    if(_tcpSocket->state()!=QTcpSocket::ConnectedState &&
       _tcpSocket->state()!=QTcpSocket::ConnectingState )
    {
        g_connect_fail_times++;
        doConnectToServer();
    }
    else
    {
        if(g_connect_fail_times>0)
            g_connect_fail_times = 0;

        if(!isSetUpPorject)
        {
            int delay = qrand()%3;
            QTimer::singleShot(delay*1000,this,SLOT(doGetProject()));
        }
    }

//    else
//    {
//        static QByteArray alive(TCP_ALIVE_FALG);
//        doWriteTcpData(alive);

//        //如果长时间没收到包活包，尝试重连接
//        if(_lastAliveTime.secsTo(QTime::currentTime()) > SECS_NO_MSG_FROM_SERVER)
//        {
//            doConnectToServer();
//        }
//    }

//    if(!_cmdParse->isRunning())
//    {
//        _cmdParse->anayseXmlData("<Gate_CONNECT />");
//    }
}

void CTcpConnect::doConnectToServer()
{
    qDebug() <<"doConnectToServer";

    if(_tcpSocket)
    {
        delete _tcpSocket;
    }

    _tcpSocket = new CSyncTcpSocket();
    _tcpSocket->moveToThread(QThread::currentThread());
    _tcpSocket->setEncryKey(ServerSetting::defaultSetting()->getAesKey());

    CNetworkThread::gServerConnected = false;

    bool con = _tcpSocket->connectHostAndVerify(
                     ServerSetting::defaultSetting()->_cloundServerIp,
                     PORT_LISTEN_GATEWAY,
                     ServerSetting::defaultSetting()->_serial,
                     WEB_ACCEPT) ;

    if(con)
    {
        qDebug() << "-- Peer Connect --";

        CNetworkThread::gServerConnected = true;

        connect(_tcpSocket,SIGNAL(s_onceData(QString)),this,SLOT(doTcpDataHandle(QString)));
        connect(_tcpSocket,SIGNAL(s_maybeDisconectFromHost()),this,SLOT(doDisconnect()));
        connect(_tcpSocket,SIGNAL(disconnected()),this,SLOT(doDisconnect()));

        //重连后更新工程
        QTimer::singleShot(2000,this,SLOT(doGetProject()));
        //test
        //doGatewayStatusUpdate("@189:300:switch:value:1/6/66:oo");
    }

//    if(!_cmdParse->isRunning())
//    {
//        qDebug() <<"_cmdParse <Gate_CONNECT />";
//        _cmdParse->anayseXmlData("<Gate_CONNECT />");
//    }
}

void CTcpConnect::doConnectToPushServer()
{
    qDebug() <<"Gateway: doConnectToPushServer " << ServerSetting::defaultSetting()->_push_ip << ":" <<
               ServerSetting::defaultSetting()->_push_serial;

    if(!ServerSetting::defaultSetting()->_push_ip.isEmpty() && !ServerSetting::defaultSetting()->_push_serial.isEmpty())
    {
        if(_pushSocket != NULL)
        {
            _pushSocket->deleteLater();
            _pushSocket = NULL;
        }

        _pushSocket = new CSyncTcpSocket();
        _pushSocket->moveToThread(QThread::currentThread());

        bool con = _pushSocket->connectHostAndVerify(ServerSetting::defaultSetting()->_push_ip,
                         PORT_PUSH_SERVER,
                         ServerSetting::defaultSetting()->_push_serial,
                         WEB_ACCEPT) ;

        if(con)
        {
            qDebug() << "Gateway: -- Push Connect --";

            connect(_pushSocket,SIGNAL(s_onceData(QString)),
                    this,SLOT(doPushDataHandle(QString)));

            return ;
        }
    }

    qDebug() << "Gateway: -- Push Connect fail --";
}

void CTcpConnect::doPushDataHandle(QString d)
{
    qDebug() << "Gateway Push Socket:" << d;
}

void CTcpConnect::doConnected()
{
    // connect server success
    qDebug() << "Tcp Connect is connect!";

//    static QByteArray serialData = ServerSetting::defaultSetting()->_serial.toLocal8Bit();
//    doWriteTcpDataAppendSharp(serialData);
}

void CTcpConnect::doDisconnectFromServer()
{
    if (_tcpSocket->state() == QAbstractSocket::ConnectedState ||
            _tcpSocket->state() == QAbstractSocket::ConnectingState)
    {
        _tcpSocket->disconnectFromHost();
        _tcpSocket->waitForDisconnected(MSEC_WAIT_FOR_TCP);
    }

    //stop knx
    //_cmdParse->anayseXmlData("<Gate_DISCONNECT />");
}

void CTcpConnect::sendDataToServer(QString data)
{
    if (_tcpSocket->state() == QAbstractSocket::ConnectedState)
    {
        _tcpSocket->appendSendData(data);
    }
    else{
        qDebug() << "send tcp data fail by unconnect state: " << data;
    }

}

void CTcpConnect::doRestart()
{
    ServerSetting::clearDefaultSetting();

    qDebug()<< "CTcpConnect: restart object connect";
    doDisconnectFromServer();
    doConnectToServer();
}

void CTcpConnect::doDisconnect()
{
    // disconnect server success
    qDebug() << "Tcp Connect discnnect!";
    isSetUpPorject = false;

    CNetworkThread::gServerConnected = false;
}

void CTcpConnect::doGetProject()
{
    QString reqUrl = ServerSetting::defaultSetting()->_project_url;
    //http://192.168.1.137:8080/TTG_ic/rest/jsonServices/showAll?projectId=1

    QString req = QString("gateway_serial=%1&default_project=1")
            .arg(ServerSetting::defaultSetting()->_serial);

    _http->httpPostSend(reqUrl,req.toUtf8());
}

void CTcpConnect::doEndGetProject()
{
    isSetUpPorject = true;
}

void CTcpConnect::doTcpDataHandle(QString d)
{
    QByteArray ret = _cmdParse.anayseJsonData(d.toLocal8Bit());
    qDebug() << "json:" << QString(d);
    if(!ret.isEmpty())
        _tcpSocket->appendSendData(ret);
}

// for local network conmunication , etc...volice anlyse
void CTcpConnect::doReadLocalUdpData()
{
    QUdpSocket *s = (QUdpSocket *)QObject::sender();

    QByteArray cmdBytes;
    cmdBytes.resize(s->pendingDatagramSize());

    QHostAddress host;
    quint16 port;
    s->readDatagram(cmdBytes.data(),cmdBytes.size(),&host,&port);

    if(!_cmdParse.anayseXmlData(cmdBytes))
    {
        s->writeDatagram("gateway unknown",host,port);
    }
}

//void CTcpConnect::doWriteTcpData(QByteArray data)
//{
//    if(_tcpSocket->isOpen())
//    {
//        if(!_tcpSocket->sendPackData(data))
//            qDebug() << "Gateway tcpSocket send timeout !";
//    }
//    else
//        qDebug() << "Gateway tcp socket not open!";
//}

void CTcpConnect::doWriteTcpData(QByteArray &data)
{
    if(_tcpSocket->isOpen())
    {
        _tcpSocket->sendData(data);
    }
}

void CTcpConnect::doWriteTcpDataAppendSharp(QByteArray data)
{
    if(_tcpSocket->isOpen())
    {
        _tcpSocket->appendSendData(data);
    }
    else
    {
        qDebug() << "Gateway tcpSocket not open !";
    }
}

void CTcpConnect::doChangeUpdateMode(QString mode)
{
    //mode change from none to other, and this is the first time to upload all the device state
    if(MODE_UPDATE_NONE == _updateMode && mode!=MODE_UPDATE_NONE)
    {
       doControlCmd(CMD_CONTROL_READ_ALL);
    }

    if(mode == MODE_UPDATE_ALL || mode == MODE_UPDATE_APP
            ||mode == MODE_UPDATE_NONE || mode == MODE_UPDATE_WEB)
    {
        _updateMode = mode;
    }
    else{
        qDebug() << "!doChangeUpdateMode: fail by unknow mode " << mode;
    }
}

static const int g_update_map_size = 10;
void CTcpConnect::doControlCmd(QString cmd)
{
    if(cmd == CMD_CONTROL_READ_ALL)
    {
        qDebug() << "CTcpConnect: CMD_CONTROL_READ_ALL";
        _cmdParse.refreshStatus();
    }
}

void CTcpConnect::doUpdateDeviceStatus()
{
    //none mode will retrun ,  the update map will be the same size as all map after a while
    if(_updateMode == MODE_UPDATE_NONE)
    {
        //qDebug() << "_updateMode none , return ";
        return ;
    }

    QString updateStr = "";
    QList<QString> keys = _updateMap.keys();
    for(int i=0;i<keys.size();i++)
    {
        updateStr += "@"+_updateMap.value(keys.at(i));
    }
    _updateMap.clear();

    if(updateStr.length()>0)
    {
        QString req = QString("%1=%2")
                .arg("update")
                .arg(updateStr);

        //we use tcp to transfor udpate message, server will change database directly
        //sendDataToServer(req);
        if(_updateMode == MODE_UPDATE_ALL ||_updateMode == MODE_UPDATE_WEB)
        {
            qDebug() << "\n ####UPDATE: change status - " << updateStr << "\n";

            QString reqUrl = ServerSetting::defaultSetting()->_update_url;
            _httpStatusUpdate.httpPostSend(reqUrl,req.toUtf8());
        }
        else
        {
            sendDataToServer(req);
        }
    }

    //用户每次登录都会重新刷新设备状态

//    if(_need_update_all)
//    {
//        QString updateStr = "";
//        QList<QString> keys = _allMap.keys();
//        int i = 0 , counter = 0;
//        for(i= _index_all_map;i<keys.size() && counter< g_update_map_size ;i++,counter++,_index_all_map++)
//        {
//            updateStr += "@"+_allMap.value(keys.at(i));
//        }

//        if(i == keys.size())
//        {
//            _need_update_all = false;
//        }

//        // send the status both http and tcp
//        if(updateStr.length()>0)
//        {
//            qDebug() << "\n ####UPDATE: all status "  << updateStr;

//            QString req = QString("%1=%2")
//                    .arg(UPDATE_DEV)
//                    .arg(updateStr);

//            CHttpManger *_upHttp = new CHttpManger();
//            _upHttp->_updateData = updateStr;
//            _upHttp->_try_times ++;

//            connect(_upHttp,SIGNAL(s_reply(QByteArray)),this,SLOT(doGatewayDeviceUpdateResult(QByteArray)));

//            QString reqUrl = ServerSetting::defaultSetting()->_update_url;
//            _upHttp->httpPostSend(reqUrl,req.toUtf8());

//            //tcp send
//            sendDataToServer(req);
//        }
//    }
}

void CTcpConnect::doGatewayDeviceStatusUpdate(QString id,QString d)
{
    _updateMap.insertMulti(id,d);
    //_allMap.insert(id,d);
}

void CTcpConnect::doGatewayStatusUpdate(QString d)
{
//    QString reqUrl = ServerSetting::defaultSetting()->_update_url;

//    QString req = QString("serial=%1&update=%2")
//            .arg(ServerSetting::defaultSetting()->_serial)
//            .arg(d);

    //_httpUpdate->httpPostSend(reqUrl,req.toUtf8());

//    QString req = QString("%1=%2")
//            .arg(GATE_KNX_STATE)
//            .arg(d);

//    sendDataToServer(req);

//    qDebug() << "Update Status:" << ServerSetting::defaultSetting()->_update_url + "?"+req;
}

void CTcpConnect::doGatewayDeviceUpdateResult(QByteArray d)
{
    CHttpManger *h = (CHttpManger *)QObject::sender();
    if(!h->_request_ok && h->_try_times<2)
    {
        qDebug() << "Http update fail, try again:" << h->_updateData;

        h->_try_times++;
        QString reqUrl = ServerSetting::defaultSetting()->_update_url;
        h->httpPostSend(reqUrl,h->_updateData.toUtf8());
    }
    else
    {
        h->deleteLater();
        qDebug() << "Status Update: " << d;
    }

}

void CTcpConnect::doGatewayUpdateResult(QByteArray d)
{
    if(d == "false")
        qDebug() << "Status Update: false";
}

void CTcpConnect::doReadProjectSetting(QByteArray d)
{
    //qDebug() << "\n\n Project From: \n" << QString(d);
    if(_cmdParse.projectContext(d))
        doEndGetProject();
    else
        qDebug() << "\n Project format error";
}

///
/// \brief CNetworkThread::CNetworkThread
/// \param parent
///
CNetworkThread::CNetworkThread(QObject *parent) :
    QThread(parent)
{
    tcpConn = NULL;
    localControl = NULL;

    _startTime = QTime::currentTime();
    connect(&_rebootTimer,SIGNAL(timeout()),this,SLOT(doRestartConnector()));
    _rebootTimer.start(60*60*1000);

    connect(&_httpUpdate,SIGNAL(s_reply(QByteArray)),this,SLOT(doUpdateMessage(QByteArray)));

    checkUpdateVersion();
}

void CNetworkThread::doUpdateMessage(QByteArray data)
{
    qDebug() << "UP:doUpdateMessage " << data;
    QJsonDocument doc = QJsonDocument::fromJson(data);
    if(doc.isObject())
    {
        QJsonArray darray = doc.object().value("data").toArray();
        if(!darray.isEmpty())
        {
            for(int i=0;i<darray.size();i++)
            {
                QJsonObject jo =  darray.at(i).toObject();
                int isDel = jo.value("deleted").toInt() ;
                QString upfileName = jo.value("fileName").toString();
                if(isDel == 0 && upfileName.startsWith("TTG-GateWay"))
                {
                    int version = jo.value("version").toString().toUInt();
                    QString md5 = jo.value("verifyMd5").toString();
                    if(version>g_version)
                    {
                        qDebug() << " doUpdateMessage :  " << version << ">" << g_version;
                        int id = jo.value("id").toInt();
                        QJsonObject jo_detail = jo.value("detailsJson").toObject();
                        int filesize = jo_detail.value("fileSize").toInt();
                        updateDownloadWithId(upfileName,md5,filesize);
                    }
                    else{
                        qDebug() << " doUpdateMessage :   cancel " << version << "<" << g_version;
                    }
                }else{
                    qDebug() <<  " doUpdateMessage :  not match " << upfileName;
                }
            }
        }
    }

}

//更换FTP下载，并校验MD5的值
void CNetworkThread::updateDownloadWithId(QString downfileName, QString md5, int filesize)
{
    qDebug() << "check updateDownloadWithId " << downfileName << " - md5 " << md5;

    QString filename = "TTG-GateWay.down";
    QFile f(filename);
    if(f.exists())
        f.remove();

    QString url = "http://"+ServerSetting::defaultSetting()->_ip_update+":11000/"+ downfileName;
    QString cmd = "wget";
    QStringList arg;
    arg << url << "-O" << filename;
    _execCmd.doCmd(cmd,arg);

    QFile up(filename);
    if(up.exists() && up.size() == filesize)
    {
        if(up.open(QIODevice::ReadOnly))
        {
            QByteArray ba = QCryptographicHash::hash(up.readAll(), QCryptographicHash::Md5);
            up.close();
            qDebug() << "updateDownload: file ok ~ " << ba.toHex().constData();

            cmd = "cp";
            arg.clear();
            arg << filename << "TTG-GateWay.new";
            _execCmd.doCmd(cmd,arg);

            arg.clear();
            _execCmd.doCmd("sync",arg);

            qDebug() << "wait for reboot  ! ";

            QTimer::singleShot(5000,this,SLOT(rebootDevice()));
        }

    }
    else
    {
        qDebug() << "updateDownload: file fail !!! ";
    }

}

void CNetworkThread::checkUpdateVersion()
{
    qDebug() << "UP: checkUpdateVersion";
    QString url = "http://"+ServerSetting::defaultSetting()->_ip_update+"/TTG_st/im/spl";
    QByteArray data = "type=1";
    _httpUpdate.httpPostSend(url,data);
}

void CNetworkThread::updateDownload(QString filename,int filesize)
{
}

void CNetworkThread::doRestartConnector()
{
    QTime curTime = QTime::currentTime();
    int secs = _startTime.secsTo(curTime);

    if(g_connect_fail_times>MAX_CONNECT_FAIL_TIMES)
    {
        _argvlist.clear();
        _argvlist << "eth0";
        _execCmd.doCmd("udhcpc",_argvlist);

        g_connect_fail_times = 0;
    }

    if(curTime.hour()%2 == 0)
        checkUpdateVersion();

    if(secs>REBOOT_INTERVAL_SECONDS)
    {
        if(curTime.hour() >= 3 &&  curTime.hour() <= 5)
        {
            rebootDevice();
        }
    }
}

void CNetworkThread::rebootDevice()
{
    if(!ServerSetting::defaultSetting()->doStopDog())
    {
        _argvlist.clear();
        _execCmd.doCmd("reboot",_argvlist);
    }
}

void CNetworkThread::run()
{
    if(ServerSetting::defaultSetting()->_system_ver == 1)
    {
        tcpConn = new CTcpConnect();
        tcpConn->moveToThread(this);
        tcpConn->doConnectToServer();

        localControl = new QUdpSocket();
        if(localControl->bind(PORT_LOCAL_NETWORK))
        {
            connect(localControl,SIGNAL(readyRead()),tcpConn,SLOT(doReadLocalUdpData()));
        }

        CTcpUpdate tcpUpdate ;
        tcpUpdate.startServer();
        connect(&tcpUpdate,SIGNAL(s_settingChange()),tcpConn,
                SLOT(doRestart()));

        exec();

        tcpConn->deleteLater();
        localControl->deleteLater();
    }
    else if(ServerSetting::defaultSetting()->_system_ver == 2)
    {
        tcpConn2 = new CTcpConnect_v2();
        tcpConn2->moveToThread(this);
        tcpConn2->doStateCheck();



//        CTcpUpdate tcpUpdate ;
//        tcpUpdate.startServer();
//        connect(&tcpUpdate,SIGNAL(s_settingChange()),tcpConn2,
//                SLOT(doRestart()));

        exec();

        tcpConn2->deleteLater();
        //localControl->deleteLater();
    }



//    CHoleMakeBeat *holeMake = new CHoleMakeBeat();

//    if(holeMake->initState)
//    {
//        connect(holeMake,SIGNAL(s_reqTcpConnect()),tcpConn,SLOT(doConnectToServer()));
//        connect(holeMake,SIGNAL(s_reqTcpDisconnect()),tcpConn,SLOT(doDisconnectFromServer()));

//        exec();
//    }

//    holeMake->deleteLater();

}
